import { CACHE_MANAGER, Cache } from "@nestjs/cache-manager";
import { Inject, Injectable, UnauthorizedException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { EventEmitter2 } from "src/common/event2";
import { UserInfo } from "src/common/model";
import { Emitter, getAuthTokenKey } from "src/constant";
import { AuthSignInDto } from "src/dto/auth";
import { UserEntity } from "src/entity";
import { WsCommonException } from "src/exception";
import { JwtService } from "src/jwt/jwt.service";
import { UserService } from "src/user/service/user.service";

@Injectable()
export class AuthService {
    constructor(
        @Inject(CACHE_MANAGER) private cacheManager: Cache,
        private readonly eventEmitter: EventEmitter2,
        private readonly configService: ConfigService,
        private readonly userService: UserService,
        private readonly jwtService: JwtService,
    ) {}

    private async cacheToken(user: UserEntity, token: string) {
        // 不允许同时在线,关闭旧的
        if (!this.configService.get<boolean>("MULTI_DEVICE_LOGIN")) {
            this.eventEmitter.emitAsync(Emitter.CLOSE_WS_BY_USER_ID, user.id, token, new WsCommonException(1105, "被挤下线了!"));
        }
        return await this.cacheManager.set(
            getAuthTokenKey(user.id),
            token,
            this.configService.get<number>("USER_TOKEN_EXPIRED") * 1000,
        );
    }

    private async generateUserToken(user: UserEntity): Promise<[string, UserInfo]> {
        const token = await this.jwtService.generateUserToken(user);

        await this.cacheToken(user, token);

        return [
            token,
            {
                ...(await this.userService.getRequestUser(user.id)),
                roles: user.roles,
            },
        ];
    }

    async signin(user: AuthSignInDto) {
        const info = await this.userService.findByName(user.username);

        if (!info) throw new UnauthorizedException("用不存在");

        if (user.password != info.password) throw new UnauthorizedException("密码错误！");

        return await this.generateUserToken(info);
    }

    async signup(user: AuthSignInDto) {
        const info = await this.userService.create({
            username: user.username,
            password: user.password,
        });

        return await this.generateUserToken(info);
    }
}
